﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using Microsoft.Office.Interop.Outlook;
using Exception = System.Exception;

namespace Addins.Outlook
{
    public class LinqQuery : IDisposable
    {
        #region declarations
        //references
        private Application _outlook;
        private NameSpace _outlookNsMapi;
        private readonly List<StoreItem> _lstStores;
        private IEnumerable<MailItem> _mails;

        //threads
        private Thread _thrStoreList;
        private Thread _thrMailsList;

        //ints
        private int _intStoreMailsCount = 0;

        //bools
        private bool _fDisposed;

        #region events
        #region store list
        public delegate void StoreListLoadedEventHandler(object sender);
        public event StoreListLoadedEventHandler StoreListLoaded;
        private void _ThrowStoreListLoadedEvent()
        {
            if (StoreListLoaded != null)
            {
                StoreListLoaded(this);
            }
        }
        #endregion

        #region mail list
        public delegate void ReadingMailFolderEventHandler(object sender, ReadingMailFolderEventArgs rmfea);
        public event ReadingMailFolderEventHandler ReadingMailFolder;
        private void _ThrowReadingMailFolderEvent(
            ReadingMailFolderEventArgs rmfea)
        {
            if (ReadingMailFolder != null)
            {
                ReadingMailFolder(this, rmfea);
            }
        }

        public delegate void MailsListReadyEventHandler(object sender);
        public event MailsListReadyEventHandler MailsListReady;
        private void _ThrowMailsListReadyEvent()
        {
            if (MailsListReady != null)
            {
                MailsListReady(this);
            }
        }
        #endregion
        #endregion
        #endregion



        #region constructor / terminator
        public LinqQuery()
        {
            //assign references
            _outlook = new Application();
            _outlookNsMapi = _outlook.GetNamespace("MAPI");

            _lstStores = new List<StoreItem>();

            //get stores
            _RefreshStoreList();
        }

        #region IDisposable Member
        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(
            bool disposing)
        {
            if (!_fDisposed)
            {
                if (disposing)
                {
                    //cleanup managed handles
                    _outlookNsMapi = null;
                    _outlook = null;
                }

                _fDisposed = true;
            }
        }
        #endregion
        #endregion



        #region private functions
        #region stores
        /// <summary>starts the StoreList thread
        /// </summary>
        /// <history>created  20.06.2012</history>
        private void _RefreshStoreList()
        {
            if (_thrStoreList != null)
            {
                _thrStoreList.Abort();
                _thrStoreList = null;
            }

            _thrStoreList = new Thread((ParameterizedThreadStart) delegate
                                                                  {
                                                                      _GetStores();
                                                                  });
            _thrStoreList.Start();
        }

        /// <summary>reads the Stores collection of the outlook session
        /// </summary>
        /// <history>created  20.06.2012</history>
        private void _GetStores()
        {
            //clear list
            _lstStores.Clear();

            //get all stores
            foreach (Store str in _outlook.Session.Stores)
            {
                _lstStores.Add(new StoreItem(str.DisplayName, str.FilePath));
            }

            //sort stores by DisplayName
            _lstStores.Sort((s1, s2) => s1.DisplayName.CompareTo(s2.DisplayName));

            //throw done event
            _ThrowStoreListLoadedEvent();
            
            //nullifiy thread
            _thrStoreList = null;
        }
        #endregion

        #region mails
        private void _RefreshMailsList(
            string strStoreDisplayName)
        {
            _thrMailsList = new Thread((ParameterizedThreadStart)delegate
            {
                _GetMailsOfStore(strStoreDisplayName);
            });
            _thrMailsList.Start();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <history>created  18.06.2012</history>
        private void _GetMailsOfStore(
            string strStoreDisplayName)
        {
            MAPIFolder rootFolder = _outlookNsMapi.Stores[strStoreDisplayName].GetRootFolder();

            _ThrowReadingMailFolderEvent(new ReadingMailFolderEventArgs(rootFolder.Name));

            _mails = rootFolder.Items.OfType<MailItem>();
            _GetFoldersMails(rootFolder);

            _ThrowMailsListReadyEvent();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fldObj"></param>
        /// <history>created  18.06.2012</history>
        private void _GetFoldersMails(
            MAPIFolder fldObj)
        {
            foreach (MAPIFolder fld in fldObj.Folders)
            {
                _ThrowReadingMailFolderEvent(new ReadingMailFolderEventArgs(fld.Name));

                _mails = _mails.Concat(fld.Items.OfType<MailItem>());

                if (fld.Folders.Count > 0)
                {
                    _GetFoldersMails(fld);
                }
            }
        }

        private void _GetMailsCount(
            MAPIFolder fldObj)
        {
            foreach (MAPIFolder fld in fldObj.Folders)
            {
                _intStoreMailsCount += fld.Items.OfType<MailItem>().Count();

                if (fld.Folders.Count > 0)
                {
                    _GetMailsCount(fld);
                }
            }
        }
        #endregion
        #endregion



        #region interface
        #region properties
        /// <summary>represents the Stores collection of the outlook session
        /// </summary>
        /// <history>created  20.06.2012</history>
        public List<StoreItem> Stores
        {
            get { return _lstStores; }
        }

        /// <summary>has all mails found by a query
        /// </summary>
        /// <history>created  21.06.2012</history>
        public IEnumerable<MailItem> Mails
        {
            get { return _mails; }
        }
        #endregion

        #region methods
        /// <summary>reads the Stores collection of the outlook session
        /// </summary>
        /// <history>created  20.06.2012</history>
        public void RefreshStoreList()
        {
            _RefreshStoreList();
        }

        /// <summary>reads the mails of the given store name
        /// </summary>
        /// <history>created  20.06.2012</history>
        public void RefreshMailsList(
            string strStoreDisplayName)
        {
            _RefreshMailsList(strStoreDisplayName);
        }
        #endregion
        #endregion
    }
}
